﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Baddies.Map;
using Baddies.Utils;
using Microsoft.Xna.Framework;

namespace BaddiesEditor.PaintTools
{
    /// <summary>
    /// Tool for using a flood fill of tiles on the map.
    /// </summary>
    public class BucketPaintTool : PaintTool
    {
        /// <summary>
        /// Initializes a new instance of the BucketPaintTool class.
        /// </summary>
        /// <param name="cursorIcon">Bitmap image for this tool.</param>
        public BucketPaintTool(System.Drawing.Bitmap cursorIcon)
            : base(cursorIcon)
        {            
        }

        /// <summary>
        /// Applies the tool effect on the map.
        /// </summary>
        /// <param name="map">Map to use the tool on.</param>
        /// <param name="tileSize">Size of the tiles in the map.</param>
        public override void Use(Baddies.Map.MapGrid map, int tileSize)
        {
            base.Use(map, tileSize);

            // if sX, sY are inside the map
            Vector3 mapPos = map.DrawPosition;

            // The -1 is there to correct the fact that if the user clicks in 
            // the very VERY edge of the map, we don't try to access a non-existing tile (N + 1).
            Rectangle mapRect = new Rectangle(
                (int)mapPos.X,
                (int)mapPos.Y,
                (map.MapWidth * map.TileSize) - 1,
                (map.MapHeight * map.TileSize) - 1);

            if (Util.PoinInRect(mapRect, (int)this.ScreenPosition.X, (int)this.ScreenPosition.Y))
            {
                // find the tile on dmX+sX, dmY+sY (where dm = map draw coords)
                MapTile initialTile = map.GetTileFromRealPos(
                    -(int)mapPos.X + (int)this.ScreenPosition.X,
                    -(int)mapPos.Y + (int)this.ScreenPosition.Y);

                // Update all the tiles with a flood fill.
                this.FloodFill(map, initialTile, tileSize);
            }
        }

        /// <summary>
        /// Sets a new texture to a given tile.
        /// </summary>
        /// <param name="tile">Tile to edit.</param>
        /// <param name="tileSize">Size of the tile.</param>
        private void SetTileTexture(MapTile tile, int tileSize)
        {
            // set it to the specified tile texture
            tile.SetDrawTexture(
                new Rectangle(
                    (int)this.SelectedTileImage.X,
                    (int)this.SelectedTileImage.Y,
                    tileSize,
                    tileSize));
        }

        /// <summary>
        /// Does a flood fill of the map using the selected tile. 4 Connected BFS.
        /// </summary>
        /// <param name="map">Map to do the fill on.</param>
        /// <param name="initialTile">Initial tile to start the fill.</param>
        /// <param name="tilesize">Size of the tiles.</param>
        private void FloodFill(MapGrid map, MapTile initialTile, int tilesize)
        {
            // Visted vector to keep track of which tiles we visited in the search.
            bool[,] visited = new bool [map.MapWidth, map.MapHeight];
            for (int x = 0; x < map.MapWidth; x++)
            {
                for (int y = 0; y < map.MapHeight; y++)
                {
                    visited[x, y] = false;
                }
            }

            // List of tiles to visit
            Queue<MapTile> toVisit = new Queue<MapTile>();
            toVisit.Enqueue(initialTile);

            // Initial tile original texture
            int originalTextureX = initialTile.MaterialArea.Left;
            int originalTextureY = initialTile.MaterialArea.Top;

            // 4 Directions (Top, Right, Bottom, Left) increment
            int[] xDir = { 0, 1, 0, -1 };
            int[] yDir = { -1, 0, 1, 0 };

            while (toVisit.Count != 0)
            {
                MapTile curTile = toVisit.Dequeue();

                // Get all the surrounding tiles 
                for (int i = 0; i < 4; i++)
                {
                    int newX = (int)curTile.TilePos.X + xDir[i];
                    int newY = (int)curTile.TilePos.Y + yDir[i];

                    // If the tile is a valid tile
                    if (map.Exists(newX, newY))
                    {
                        // That have not been visited
                        if (!visited[newX, newY])
                        {
                            MapTile newTile = map[newX, newY];

                            // and that have the same texture as the original tile
                            if (newTile.MaterialArea.Left == originalTextureX &&
                                newTile.MaterialArea.Top == originalTextureY)
                            {
                                toVisit.Enqueue(newTile);
                            }
                        }                    
                    }

                    // Mark texture as visited
                    visited[(int)curTile.TilePos.X, (int)curTile.TilePos.Y] = true;

                    // Set the texture image to the new image
                    this.SetTileTexture(curTile, tilesize);
                }
            }
        }
    }
}
